package services

import (
	"database/sql"
	"fmt"
	"strings"

	"cashew.com/cashew_common/configs"
	"cashew.com/cashew_common/constants"
	"cashew.com/cashew_common/utils"
	"cashew.com/cashew_task/dtos"
	"gitee.com/go-package/carbon"
	"github.com/beego/beego/v2/core/logs"
	"github.com/pkg/errors"
)

// 适用于 maoyin 系统

// 声明不同地区时区和北京时间的差距
var timeMaoyinOffsetMap = map[string]int{
	"86":  0,               // 中国
	"886": 0,               // 台湾
	"62":  1 * 60 * 60,     // 印度尼西亚
	"14":  3*60*60 + 30*60, // 阿富汗
	"244": 7 * 60 * 60,     // 安哥拉,
}

// 批量计算主播评级并更新数据库
/**
每周一0点10分，对主播进行评级，按上一周的以下标准分为四个等级：S、A、B、C
分级标准：
S：被拨打接通率不低于90%；收益不低于73500积分（每天通话30分钟）；在线与接单状态相加时长不低于1050分钟（每天2.5小时）；
A：被拨打接通率不低于80%；收益不低于49000积分（每天通话20分钟）；在线与接单状态相加时长不低于1050分钟（每天2.5小时）；
B：认证通过7天内的新主播
C：不满足其他三项的
被拨打接通率=被拨打接通电话次数 / 被拨打通话请求次数 *100%
*/
func AnchorMaoyinRating(periodDays int, ratingDate string, regionId int) error {

	if periodDays == 0 {
		periodDays = 7
	}

	if regionId == 0 {
		regionId = 62 // 默认印度尼西亚
	}

	timeOffset := timeOffsetMap[fmt.Sprintf("%d", regionId)]

	query := map[string]string{
		"sub_region_id": fmt.Sprintf("%d", regionId),
	}
	fields := []string{"anchor_id", "rating"}
	sortBy := []string{"anchor_id"}
	order := []string{"asc"}
	limit := int64(10)
	offset := int64(0)

	endTime := carbon.SetTimezone("Asia/Shanghai").Now().StartOfDay().Timestamp()
	if ratingDate != "" {
		endTime = carbon.SetTimezone("Asia/Shanghai").Parse(ratingDate).StartOfDay().Timestamp()
	}
	endTime = endTime + int64(timeOffset)

	for {
		list, err := GetMaoyinAllAnchorInfo(query, fields, sortBy, order, offset, limit)
		if err != nil {
			return err
		}

		if len(list) == 0 {
			// 没有更多数据，退出循环
			break
		}

		for _, anchor := range list {
			anchorID := anchor["anchor_id"].(int64)
			oldRating := anchor["rating"].(int64)

			// 打印主播信息
			logs.Info("主播ID：%d, 原等级：%d", anchorID, oldRating)

			// 实现主播评级逻辑
			// 根据评级标准计算主播等级，并更新数据库
			// 这里应该调用一个函数来执行评级和更新操作
			err = handleMaoyinRating(anchorID, periodDays, oldRating, endTime)
			if err != nil {
				logs.Error("计算主播评级失败：handleRating(%d, %d, %d, %d) error: %v", anchorID, periodDays, oldRating, endTime, err)
			}
		}

		// 更新offset以获取下一页数据
		offset += limit
	}

	return nil
}

// 根据主播id,统计该主播的评级信息
func handleMaoyinRating(anchorID int64, periodDays int, oldRating int64, endTime int64) (err error) {
	anchorRatingBNewAnchorDays := configs.AnchorRatingBNewAnchorDays

	// 统计主播的评级信息
	startTime := endTime - int64(periodDays*24*60*60)

	// 如果是新主播，不超过7天，不参与评级，直接返回
	if oldRating == constants.ANCHOR_GRADE_B {
		auditInfo, er := GetMaoyinVideoCallMaterialsStatAnchorByAnchorId(anchorID)
		if er != nil {
			logs.Error("获取主播审核信息失败：%v", er)
			return er
		}

		if auditInfo == nil {
			logs.Error("主播:%d 审核信息为空", anchorID)
			return errors.New(fmt.Sprintf("主播:%d 审核信息为空", anchorID))
		}
		firstAuditPassTime := auditInfo.FirstAuditPassTime

		if endTime-firstAuditPassTime < int64(anchorRatingBNewAnchorDays*24*60*60) {
			logs.Error("主播ID：%d，新主播不超过7天，不参与评级", anchorID)
			return nil
		}
	}

	anchorRatingSCalledRate := configs.AnchorRatingSCalledRate
	anchorRatingSScore := configs.AnchorRatingSScore
	anchorRatingSOnlineDurationMinutes := configs.AnchorRatingSOnlineDurationMinutes
	anchorRatingACalledRate := configs.AnchorRatingACalledRate
	anchorRatingAScore := configs.AnchorRatingAScore
	anchorRatingAOnlineDurationMinutes := configs.AnchorRatingAOnlineDurationMinutes

	anchorGrade := constants.ANCHOR_GRADE_C

	// 1. 被拨打接通率 = 被拨打接通电话次数 / 被拨打通话请求次数 *100%
	calledRequestCount, err := GetMaoyinAnchorWorkStatsByTime(anchorID, constants.ANCHOR_STAT_TYPE_CALLED_REQUEST_COUNT, startTime, endTime)
	if err != nil {
		logs.Error("获取主播被拨打通话请求次数失败：%v", err)
		return err
	}
	calledConnectedCount, err := GetMaoyinAnchorWorkStatsByTime(anchorID, constants.ANCHOR_STAT_TYPE_CALLED_CONNECTED_COUNT, startTime, endTime)
	if err != nil {
		logs.Error("获取主播被拨打接通电话次数失败：%v", err)
		return err
	}
	calledRate := 0
	if calledRequestCount > 0 {
		calledRate = int(float64(calledConnectedCount) / float64(calledRequestCount) * 100)
	}

	// 2. 收益
	callScore, err := GetMaoyinAnchorWorkStatsByTime(anchorID, constants.ANCHOR_STAT_TYPE_CALL_SCORE, startTime, endTime)
	if err != nil {
		logs.Error("获取主播通话积分失败：%v", err)
		return err
	}

	giftScore, err := GetMaoyinAnchorWorkStatsByTime(anchorID, constants.ANCHOR_STAT_TYPE_IM_GIFT_SCORE, startTime, endTime)
	if err != nil {
		logs.Error("获取主播礼物积分失败：%v", err)
		return err
	}

	// 3. 在线与接单状态相加时长(改成每天2.5小时)
	allGreaterS := true
	allGreaterA := true

	for i := 0; i < periodDays; i++ {
		onlineDurationMinutes, err := GetMaoyinAnchorWorkStatsByTime(anchorID, constants.ANCHOR_STAT_TYPE_ONLINE_DURATION_MINUTES, startTime+int64(i*24*60*60), startTime+int64((i+1)*24*60*60))
		if err != nil {
			logs.Error("获取主播在线时长失败：%v", err)
			return err
		}
		logs.Info("主播ID：%d，第%d天在线时长：%d分钟", anchorID, i+1, onlineDurationMinutes)
		if onlineDurationMinutes < anchorRatingSOnlineDurationMinutes {
			allGreaterS = false
		}

		if onlineDurationMinutes < anchorRatingAOnlineDurationMinutes {
			allGreaterA = false
			break
		}
	}

	if allGreaterS && calledRate >= anchorRatingSCalledRate && callScore+giftScore >= anchorRatingSScore {
		anchorGrade = constants.ANCHOR_GRADE_S
	} else if allGreaterA && calledRate >= anchorRatingACalledRate && callScore+giftScore >= anchorRatingAScore {
		anchorGrade = constants.ANCHOR_GRADE_A
	}

	logs.Info("主播ID：%d，评级：%d", anchorID, anchorGrade)

	// 更新数据库
	anchorNew := dtos.Anchor_info{
		AnchorId: anchorID,
		Rating:   anchorGrade,
	}

	// mq 同步用户信息
	go SyncUserData(fmt.Sprintf("%d", anchorID), constants.DATA_UPDATED_ANCHOR_RATING_UPDATED, nil)

	return UpdateMaoyinAnchorInfoByAnchorIdAndFields(&anchorNew, []string{"rating"})
}

func GetMaoyinAllAnchorInfo(query map[string]string, fields []string, sortby []string, order []string, offset int64, limit int64) (result []map[string]interface{}, err error) {
	db := utils.GetDB()
	if db == nil {
		return nil, errors.New("db is nil")
	}

	selectFields := strings.Join(fields, ", ")
	if selectFields == "" {
		selectFields = "*"
	}

	var whereParts []string
	var args []interface{}
	for k, v := range query {
		whereParts = append(whereParts, fmt.Sprintf("%s = ?", k))
		args = append(args, v)
	}
	whereClause := ""
	if len(whereParts) > 0 {
		whereClause = "WHERE " + strings.Join(whereParts, " AND ")
	}

	var orderByParts []string
	for i, s := range sortby {
		if i < len(order) {
			orderByParts = append(orderByParts, fmt.Sprintf("%s %s", s, order[i]))
		}
	}
	orderBy := ""
	if len(orderByParts) > 0 {
		orderBy = "ORDER BY " + strings.Join(orderByParts, ", ")
	}

	limitOffset := fmt.Sprintf("LIMIT %d OFFSET %d", limit, offset)

	sqlStmt := fmt.Sprintf("SELECT %s FROM anchor_info %s %s %s", selectFields, whereClause, orderBy, limitOffset)

	rows, err := db.Query(sqlStmt, args...)
	if err != nil {
		return nil, fmt.Errorf("query failed: %v", err)
	}
	defer rows.Close()

	columns, _ := rows.Columns()
	for rows.Next() {
		values := make([]interface{}, len(columns))
		pointers := make([]interface{}, len(columns))
		for i := range values {
			pointers[i] = &values[i]
		}

		if err := rows.Scan(pointers...); err != nil {
			return nil, fmt.Errorf("scan row failed: %v", err)
		}

		entry := make(map[string]interface{})
		for i, col := range columns {
			entry[col] = values[i]
		}
		result = append(result, entry)
	}

	return result, nil
}

func GetMaoyinVideoCallMaterialsStatAnchorByAnchorId(anchorId int64) (*dtos.Video_call_materials_stat_anchor, error) {
	db := utils.GetDB()
	if db == nil {
		return nil, errors.New("db is nil")
	}

	sqlStmt := "SELECT first_audit_pass_time FROM video_call_materials_stat_anchor WHERE anchor_id = ?"
	row := db.QueryRow(sqlStmt, anchorId)

	var firstAuditPassTime int64
	if err := row.Scan(&firstAuditPassTime); err != nil {
		if err == sql.ErrNoRows {
			return nil, nil
		}
		return nil, fmt.Errorf("scan failed: %v", err)
	}

	return &dtos.Video_call_materials_stat_anchor{
		AnchorId:           anchorId,
		FirstAuditPassTime: firstAuditPassTime,
	}, nil
}

func GetMaoyinAnchorWorkStatsByTime(anchorId int64, statsType constants.StatType, startTime int64, endTime int64) (int64, error) {
	db := utils.GetDB()
	if db == nil {
		return 0, errors.New("db is nil")
	}

	sqlStmt := `
        SELECT COALESCE(SUM(value), 0) 
        FROM anchor_work_stats 
        WHERE anchor_id = ? 
          AND type = ? 
          AND hour_id >= ? 
          AND hour_id < ?
    `
	var value int64
	err := db.QueryRow(sqlStmt, anchorId, int(statsType), startTime, endTime).Scan(&value)
	if err != nil {
		return 0, fmt.Errorf("query failed: %v", err)
	}

	return value, nil
}

func UpdateMaoyinAnchorInfoByAnchorIdAndFields(m *dtos.Anchor_info, fields []string) error {
	db := utils.GetDB()
	if db == nil {
		return errors.New("db is nil")
	}

	var setClauses []string
	var args []interface{}
	for _, field := range fields {
		switch field {
		case "rating":
			setClauses = append(setClauses, "rating = ?")
			args = append(args, m.Rating)
		default:
			return fmt.Errorf("unsupported field: %s", field)
		}
	}

	if len(setClauses) == 0 {
		return errors.New("no fields to update")
	}

	args = append(args, m.AnchorId)
	sqlStmt := fmt.Sprintf(
		"UPDATE anchor_info SET %s WHERE anchor_id = ?",
		strings.Join(setClauses, ", "),
	)

	if _, err := db.Exec(sqlStmt, args...); err != nil {
		return fmt.Errorf("exec failed: %v", err)
	}

	return nil
}
